﻿using Mapster;
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Threading;
using System.Threading.Tasks;
using UpperComputer.Core.Entities;
using UpperComputer.Core.Extensions;
using YFurion.Application;
using YFurion.Extensions;
using YFurion.Session;
using YFurion.Session.Extensions;
using YFurion.Utils;

namespace UpperComputer.Application
{
    public class AuthorizeService : EFCoreServiceBase, IAuthorizeService
    {
        private readonly ILoginInOrOutLogService _loginInOrOutLogService;
        private IBaseRoleUserService _roleUserService;

        public AuthorizeService(
            ILoginInOrOutLogService loginInOrOutLogService,
            IBaseRoleUserService roleUserService
        )
        {
            _loginInOrOutLogService = loginInOrOutLogService;
            _roleUserService = roleUserService;
        }
        public async Task<VerificationResultDto> GetVerificationCode(CancellationToken cancellationToken = default)
        {
            string code;
            byte[] codeArray = VierificationCodeUtils.Create(out code);
            string base64 = "data:image/png;base64," + Convert.ToBase64String(codeArray);
            string verificationIdentifier = YIDGenerator.NextId().ToString();
            await SetCacheAsync(verificationIdentifier, code, cancellationToken, new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)
            });
            return new VerificationResultDto
            {
                Base64VerificationCode = base64,
                VerificationIdentifier = verificationIdentifier
            };
        }

        public async Task<LogoutDto> Login(LoginDto input, CancellationToken cancellationToken = default)
        {
            DistributedCache.TryGetValue(input.VerificationIdentifier, out string verificationCode);
            if (verificationCode.IsNullOrWhiteSpace())
            {
                throw Oh("验证码不正确");
            }
            if (!verificationCode.ToLower().Equals(input.VerificationCode.ToLower()))
            {
                throw Oh("验证码不正确");
            }
            await RemoveCacheAsync(input.VerificationIdentifier, cancellationToken);
            BaseUserResultDto baseUserResultDto = await LoginWithNoVerificationCode(input, cancellationToken);
            LogoutDto logoutDto = new LogoutDto
            {
                BaseUserResultDto = baseUserResultDto,
                TokenResultDto = CreateToken(baseUserResultDto.Id)
            };
            return logoutDto;
        }

        public async Task<BaseUserResultDto> LoginWithNoVerificationCode(LoginBaseDto input, CancellationToken cancellationToken = default)
        {
            string pwd = input.Password.Length == 32 ? input.Password : input.Password.GetMD5SuffixPassword();
            pwd = pwd.GetMD5PrefixPassword();
            BaseUser user = await MSRepository.Slave1<BaseUser>().FirstOrDefaultAsync(bu => bu.Account.Equals(input.Username) && bu.Password.Equals(pwd), cancellationToken: cancellationToken);
            if (user == null)
            {
                throw Oh("用户名或密码无效");
            }
            if (!user.IsActive)
            {
                throw Oh("该用户已被禁用");
            }
            //验证有没有权限登录
            if (!input.Username.ToLower().Equals("Admin"))
            {
                if (!await _roleUserService.HasPermission(input.Username, cancellationToken))
                {
                    throw Oh("用户名或密码无效");
                }
            }
            BaseUserResultDto baseUserResultDto = user.Adapt<BaseUserResultDto>();
            LoginInOrOutLogDto loginInOrOutLogDto = await _loginInOrOutLogService.CreateLog(baseUserResultDto, cancellationToken);
            return baseUserResultDto;
        }

        public async Task Logout(CancellationToken cancellationToken = default)
        {
            await _loginInOrOutLogService.CreateLogout(cancellationToken);
        }

        private TokenResultDto CreateToken(long subjectId)
        {
            SecurityTokenResult securityTokenResult = YSession.CreateToken(subjectId);
            return securityTokenResult.Adapt<TokenResultDto>();
        }
    }
}
